package com.logic.modular.game.draw.service;

import com.comment.exception.ServiceException;
import com.logic.comment.util.RedisUtils;
import com.logic.modular.game.draw.domain.PlayerDraw;
import com.comment.pojo.domain.logic.domain.PlayerHero;
import com.comment.pojo.domain.logic.domain.PlayerSkill;
import com.logic.modular.game.player.bag.service.PlayerPropType1ServiceImpl;
import com.logic.modular.game.player.bag.service.PlayerPropType2ServiceImpl;
import com.logic.modular.game.draw.mapper.PlayerDrawMapper;
import com.logic.modular.game.player.hero.service.PlayerHeroServiceImpl;
import com.logic.modular.game.player.skill.PlayerSkillServiceImpl;
import com.comment.pojo.domain.logic.core.CoreDraw;
import com.comment.pojo.domain.logic.core.CoreDrawConfig;
import com.comment.pojo.domain.logic.core.CoreDrawPool;
import com.logic.modular.gameCore.pay.drawCards.service.impl.CoreDrawConfigServiceImpl;
import com.logic.modular.gameCore.pay.drawCards.service.impl.CoreDrawPoolServiceImpl;
import com.logic.modular.gameCore.pay.drawCards.service.impl.CoreDrawServiceImpl;
import lombok.extern.slf4j.Slf4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import static com.logic.modular.game.GameConstant.PLAYER_DRAW_KEY;

/**
 * 玩家抽卡信息(PlayerDraw)表服务实现类
 *
 * @author cxy
 * @since 2024-08-08 10:42:41
 */
@Slf4j
@Service
public class PlayerDrawServiceImpl {
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private PlayerDrawMapper playerDrawMapper;
    @Resource
    private CoreDrawServiceImpl coreDrawService;
    @Resource
    private CoreDrawPoolServiceImpl coreDrawPoolServiceImpl;
    @Resource
    private CoreDrawConfigServiceImpl coreDrawConfigServiceImpl;
    @Resource
    private PlayerHeroServiceImpl playerHeroServiceImpl;
    @Resource
    private PlayerSkillServiceImpl accountSkillServiceImpl;
    @Resource
    private PlayerPropType1ServiceImpl playerPropType1Service;
    @Resource
    private PlayerPropType2ServiceImpl playerPropType2ServiceImpl;

    public Map<Object, Object> playerDrawMap(Long playerId, Long type) {
        String Key = String.format(PLAYER_DRAW_KEY, playerId, type);
        Map<Object, Object> entries = redisUtils.hGetAll(Key);
        if (entries.isEmpty()) {
            LambdaQueryWrapper<PlayerDraw> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(PlayerDraw::getType, type);
            wrapper.eq(PlayerDraw::getUid, playerId);
            entries = playerDrawMapper.selectList(wrapper).stream().collect(
                    Collectors.toMap(key -> key.getLv().toString(), value -> value));
            redisUtils.hPutAll(Key, entries, 1, TimeUnit.DAYS);
        }
        return entries;
    }

    public void updateById(PlayerDraw entity) {
        int i = playerDrawMapper.updateById(entity);

        if (i > 0) {
            saveCache(entity);
        }
    }

    private void saveCache(PlayerDraw entity) {
        String Key = String.format(PLAYER_DRAW_KEY, entity.getUid(), entity.getType());
        redisUtils.hPut(Key, entity.getLv().toString(), entity, 1, TimeUnit.DAYS);
    }

    @Transactional
    public List<?> draw(Long playerId, Long type, Integer num) {// 总次数
        LambdaQueryWrapper<PlayerDraw> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PlayerDraw::getUid, playerId);
        List<PlayerDraw> playerDraws = playerDrawMapper.selectList(wrapper);
        List<CoreDraw> coreDraws = coreDrawService.listCache();
        if (playerDraws.isEmpty() || playerDraws.size() != coreDraws.size()) {
            coreDraws.forEach(coreDraw -> {
                LambdaQueryWrapper<PlayerDraw> wrapper1 = new LambdaQueryWrapper<>();
                wrapper1.eq(PlayerDraw::getUid, playerId);
                wrapper1.eq(PlayerDraw::getType, coreDraw.getType());
                wrapper1.eq(PlayerDraw::getLv, coreDraw.getLv());
                PlayerDraw one = playerDrawMapper.selectOne(wrapper1);
                if (one == null) {
                    PlayerDraw playerDraw = new PlayerDraw();
                    playerDraw.setUid(playerId);
                    playerDraw.setType(coreDraw.getType());
                    playerDraw.setLv(coreDraw.getLv());
                    playerDraw.setTime(0L);
                    playerDraw.setTotalTime(0L);
                    playerDrawMapper.insert(playerDraw);
                }
            });
        }

        CoreDrawConfig coreDrawConfig = coreDrawConfigServiceImpl.getCache(type);
        if (coreDrawConfig == null) {
            throw new ServiceException(String.format("抽卡类型:%d,不存在", type));
        }

        Map<Long, Integer> price;
        if (num == 1) {
            price = coreDrawConfig.getOnePrice();
        } else if (num == 10) {
            price = coreDrawConfig.getTenPrice();
        } else {
            throw new ServiceException("不支持的抽卡数量");
        }

        // 付钱
        long payPropId = 2010L;
        BigDecimal payValue = new BigDecimal(price.get(2010L));
        boolean b = playerPropType2ServiceImpl.tryPayProp(playerId, payPropId, payValue);
        if (!b) {
            payPropId = 1L;
            payValue = BigDecimal.valueOf(price.get(1L));
            b = playerPropType1Service.tryPay(playerId, payPropId, payValue);
            if (!b) {
                throw new ServiceException("元宝不足");
            }
        }


        // 保底
        Map<Integer, Integer> times = coreDrawConfig.getTime();

        ArrayList<Long> rewardIds = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            CoreDraw coreDraw = null;

            // 尝试获取保底
            for (Object value : playerDrawMap(playerId, type).values()) {
                PlayerDraw playerDraw = (PlayerDraw) value;
                Long unGet = playerDraw.getTime();
                Integer valueLv = playerDraw.getLv();

                Integer mushGetTimes = times.get(valueLv);
                if (mushGetTimes == null){
                    log.info("类型:{},稀有度:{},保底未配置", type, valueLv);
                    continue;
                }
                if (unGet > mushGetTimes) { // 满足保底
                    coreDraw = coreDrawService.getCache(type, valueLv);
                }
            }
            if (coreDraw == null) {// 未触发保底开始抽卡
                coreDraw = coreDrawService.draw(type);
            }
            Integer obtainItemLv = coreDraw.getLv();
            Long obtainItemType = coreDraw.getType();

            // 更新累计未出货次数
            Map<Object, Object> playerDrawMap = playerDrawMap(playerId, type);
            for (Object value : playerDrawMap.values()) {
                PlayerDraw playerDraw = (PlayerDraw) value;
                if (!playerDraw.getLv().equals(obtainItemLv)) {
                    playerDraw.time += 1;
                    updateById(playerDraw);
                }
            }
            // 更新累计获取数量
            PlayerDraw playerDraw = (PlayerDraw) playerDrawMap.get(coreDraw.getLv().toString());
            if (playerDraw == null) {
                playerDraw = new PlayerDraw();
                playerDraw.setLv(obtainItemLv);
                playerDraw.setType(obtainItemType);
                playerDraw.setUid(playerId);
                playerDraw.setTime(0L);
                playerDraw.setTotalTime(1L);
                if (playerDraw.insert()) {
                    saveCache(playerDraw);
                }
            } else {
                playerDraw.setTime(0L);
                playerDraw.totalTime += 1;
                updateById(playerDraw);
            }

            obtainReward(playerId, type, coreDraw);
            rewardIds.add(coreDraw.getCoreDrawPool().getRewardId());
        }
        return rewardIds;
    }

    private void obtainReward(Long playerId, Long type, CoreDraw coreDraw) {
        CoreDrawPool coreDrawPool = coreDrawPoolServiceImpl.draw(coreDraw.getPool());
        coreDraw.setCoreDrawPool(coreDrawPool);
        Long rewardId = coreDrawPool.getRewardId();
        if (type == 1L) {
            PlayerHero accountHero = playerHeroServiceImpl.obtainHero(playerId, rewardId, 1);
            log.info("玩家{}抽卡获得武将{}", playerId, accountHero);
        } else if (type == 2L) {
            PlayerSkill accountSkill = accountSkillServiceImpl.obtainSkill(playerId, rewardId);
            log.info("玩家{}抽卡获得技能{}", playerId, accountSkill);
        } else {
            throw new ServiceException("不支持的抽卡类型");
        }
    }
}
